#include <stdio.h>
#include <tchar.h> // Make all functions UNICODE safe.
#include <windows.h>  
#include <setupapi.h> // for SetupDiXxx functions.
#include "Shlwapi.h"

BOOL GetHardwareStringData(
       HDEVINFO DeviceInfoSet,
       PSP_DEVINFO_DATA DeviceInfoData,
       DWORD Property,
       LPTSTR szDATA,
       DWORD dwLength,
       PDWORD dwSizeRequired);
       
BOOL GetOemInfFileName(
       HDEVINFO DeviceInfoSet,
       PSP_DEVINFO_DATA DeviceInfoData,
       LPTSTR szDATA,
       DWORD dwLength,
       PDWORD dwSizeRequired);
       
BOOL GetDeviceServiceName(
       HDEVINFO DeviceInfoSet,
       PSP_DEVINFO_DATA DeviceInfoData,
       LPTSTR szDATA,
       DWORD dwLength,
       PDWORD dwSizeRequired);
       
BOOL GetServiceData(
	LPCTSTR szServiceName,
	LPCTSTR szDataName,
       	LPTSTR szDATA,
       	DWORD dwLength,
       	PDWORD dwSizeRequired);

BOOL GetServiceDataEx(
	LPCTSTR szServiceName,
	LPCTSTR szDataName,
       	LPTSTR szDATA,
       	DWORD dwLength,
       	PDWORD dwSizeRequired);

BOOL GetDvcSvcNameByHwId(
	HKEY RootKey,
	LPCTSTR szHardwareID,
	LPTSTR szData,
	DWORD dwLength);
	
BOOL IsNumeric(LPTSTR szData);

int __cdecl _tmain(int argc, _TCHAR **argv, _TCHAR **envp)
{
    HDEVINFO DeviceInfoSet;
    SP_DEVINFO_DATA DeviceInfoData;
    DWORD i;
    BOOL Found, B_Ret = FALSE;
    char szWindir[MAX_PATH] = {'\0'};
    char szOemFile[MAX_PATH] = {'\0'};
    char szSvcName[MAX_PATH] = {'\0'};
    char szDriverImage[MAX_PATH] = {'\0'};
    char szHardwareID[MAX_PATH] = {'\0'};
    char szTargetFileName[MAX_PATH] = {'\0'};
    char szEnumPath[MAX_PATH] = {'\0'};
    HKEY EnumRootKey;

    //
    // Verify input Arguments.
    //
    if (argc < 2)
    {
        printf("usage: remove <Hardware_ID>\r\n");
        return 1;
    }

    if (GetEnvironmentVariable(
               (LPCTSTR)"windir",
               szWindir,
               1024) == 0) {
       printf("Can't get environment variable : windir\r\n");
       return 1;
    }

    //Build Enum path in registry database
    sprintf(
    	szEnumPath,
  	"%s",
  	"SYSTEM\\CurrentControlSet\\Enum");

    //
    // Create a Device Information Set
    // with all present devices
    //
    DeviceInfoSet = SetupDiGetClassDevs(
                        NULL, 0, 0,
                        DIGCF_ALLCLASSES | 
                        DIGCF_PRESENT );
    if (DeviceInfoSet == INVALID_HANDLE_VALUE)
    {
        printf("Failed to build Device \
               Information Set\r\n");
        return 1;
    }

    //
    //  Enumerate through all Devices.
    //
    DeviceInfoData.cbSize = 
    	sizeof(SP_DEVINFO_DATA);
    for (i=0;
        SetupDiEnumDeviceInfo(
        DeviceInfoSet,i,&DeviceInfoData);i++)
    {
        BOOL B_OK = FALSE;
        DWORD dwSizeRequired;
        LPTSTR p = NULL;
	//Get Service Name
        ZeroMemory(szSvcName, MAX_PATH);
        B_OK = GetHardwareStringData(
                     DeviceInfoSet,
                     &DeviceInfoData,
                     SPDRP_SERVICE,
                     szSvcName,
                     MAX_PATH,
                     &dwSizeRequired);
	/*
	B_OK = GetDeviceServiceName(
       			DeviceInfoSet,
       			&DeviceInfoData,
       			szSvcName,
       			MAX_PATH,
      			&dwSizeRequired);
      	*/
        if (B_OK == TRUE)
        {
	  //Get HardwareID
          ZeroMemory(szHardwareID, MAX_PATH);
          B_OK = GetHardwareStringData(
            	         DeviceInfoSet,
                	 &DeviceInfoData,
                     	 SPDRP_HARDWAREID,
                         szHardwareID,
                         MAX_PATH,
                         &dwSizeRequired);

	  if (B_OK == TRUE)
          {
             for (p=szHardwareID;*p&&(p<&szHardwareID[dwSizeRequired]);p+=lstrlen(p)+sizeof(char))
             {
                printf("Compare device ID: [%s]\r\n",p);

                if (strcmp(argv[1],p) == 0)
                {
                    printf("Found! [%s]\r\n",p);
                    Found = TRUE;
                    
                    /*
                    //Get Service Name by Hardware ID
 		    RegOpenKeyEx(
  			HKEY_LOCAL_MACHINE,
  			(LPCTSTR)szEnumPath,
  			0,
  			KEY_READ,
  			&EnumRootKey);
  		    GetDvcSvcNameByHwId(
			EnumRootKey,
			argv[1],
			szSvcName,
			MAX_PATH);
		    */
		    printf("We get the service - %s for Device\r\n", szSvcName);
                    //Get Service ImagePath
                    ZeroMemory(szDriverImage, MAX_PATH);
           	    B_OK = GetServiceDataEx(
				szSvcName,
				(LPCTSTR)"ImagePath",
       				szDriverImage,
       				MAX_PATH,
       				&dwSizeRequired);
       		    printf("Service Binary Image : %s\r\n", szDriverImage);
                    //Get InfFile Name
                    ZeroMemory(szOemFile, MAX_PATH);
                    B_OK = GetOemInfFileName(
                           	    DeviceInfoSet,
                           	    &DeviceInfoData,
                           	    szOemFile,
                           	    MAX_PATH,
                           	    &dwSizeRequired)|| B_OK;
                    if (B_OK == TRUE)
                    {
                      if (!SetupDiCallClassInstaller(
                              		DIF_REMOVE,
                              		DeviceInfoSet,
                              		&DeviceInfoData))
                      {
                          printf("Remove Device & Driver Failed/r/n");
                      } else {
                      	  //Delete oemxx.inf
                          sprintf(szTargetFileName, "%s\\inf\\%s.inf",
                                    szWindir, szOemFile);
                          printf("remove %s\r\n", szTargetFileName);
                          DeleteFile(szTargetFileName);
                          //Delete oemxx.pnf
                          sprintf(szTargetFileName, "%s\\inf\\%s.pnf",
                                    szWindir, szOemFile);
                          printf("remove %s\r\n", szTargetFileName);
                          DeleteFile(szTargetFileName);
                          //Delete Driver Image
                          sprintf(szTargetFileName, "%s\\%s",
                                    szWindir, szDriverImage);
                          printf("remove %s\r\n", szTargetFileName);
                          DeleteFile(szTargetFileName);
                          printf("Remove Device & Driver Completed\r\n");
                          B_Ret = TRUE;
                      }
                    } else {
                      B_Ret = FALSE;
                    }
                    break;
                }
             }//end for
            }
        }
        if (Found == TRUE)
           break;
    }

    SetupDiDestroyDeviceInfoList(DeviceInfoSet);

    return B_Ret;
}


BOOL GetHardwareStringData(
       HDEVINFO DeviceInfoSet,
       PSP_DEVINFO_DATA DeviceInfoData,
       DWORD Property,
       LPTSTR szDATA,
       DWORD dwLength,
       PDWORD dwSizeRequired)
{
    DWORD DataT;
    LPTSTR buffer = NULL;
    DWORD buffersize = 0;
    LPTSTR szHwData = NULL;
    BOOL B_Ret = FALSE;

    while (!SetupDiGetDeviceRegistryProperty(
            DeviceInfoSet,
            DeviceInfoData,
            Property,
            &DataT,
            (PBYTE)buffer,
            buffersize,
            &buffersize))
    {
      if (GetLastError() ==
                 ERROR_INVALID_DATA)
      {
         return B_Ret;
         break;
      }
      else if (GetLastError() ==
                  ERROR_INSUFFICIENT_BUFFER)
      {
        if (buffer)
          LocalFree(buffer);
        buffer = LocalAlloc(
                    LPTR,buffersize);
      } else {
        printf("Get Hardware Data Error\r\n");
        LocalFree(buffer);
        return B_Ret;
      }
    }

    if (dwLength >= buffersize)
    {
       strcpy (szDATA, buffer);
       B_Ret = TRUE;
    } else {
       B_Ret = FALSE;
    }

    *dwSizeRequired = buffersize;
    LocalFree(buffer);

    return B_Ret;
}

BOOL GetOemInfFileName(
       HDEVINFO DeviceInfoSet,
       PSP_DEVINFO_DATA DeviceInfoData,
       LPTSTR szDATA,
       DWORD dwLength,
       PDWORD dwSizeRequired)
{
    BOOL B_Ret = FALSE;
    HKEY  SwKey;
    LONG l_Val;
    DWORD dwRegType = 0;

    SwKey = SetupDiOpenDevRegKey(
                 DeviceInfoSet,
                 DeviceInfoData,
                 DICS_FLAG_GLOBAL,
                 0,
                 DIREG_DRV,
                 KEY_READ);

    if (SwKey == INVALID_HANDLE_VALUE)
    {
       printf("Open Software Key Failed\r\n");
       return FALSE;
    }
    l_Val = RegQueryValueEx(
              SwKey,
              (LPCTSTR)"InfPath",
              0,
              &dwRegType,
              (LPBYTE)szDATA,
              dwSizeRequired);
    if (l_Val == ERROR_SUCCESS)
    {
       B_Ret = StrTrim(
       	 	  szDATA,
    	          (LPCTSTR)".inf");
       if (B_Ret)
       	  printf("Oem InfFile Name : %s\r\n", szDATA);
       else
          printf("Oem InfFile Name Manipulate failed\r\n");
    } else {
       printf("Retrieve InfFile Failed\r\n");
    }  
    return B_Ret;
}

BOOL GetDeviceServiceName(
       HDEVINFO DeviceInfoSet,
       PSP_DEVINFO_DATA DeviceInfoData,
       LPTSTR szDATA,
       DWORD dwLength,
       PDWORD dwSizeRequired)
{
    BOOL B_Ret = FALSE;
    HKEY  SwKey;
    LONG l_Val;
    DWORD dwRegType = 0;

    SwKey = SetupDiOpenDevRegKey(
                 DeviceInfoSet,
                 DeviceInfoData,
                 DICS_FLAG_GLOBAL,
                 0,
                 DIREG_DRV,
                 KEY_READ);

    if (SwKey == INVALID_HANDLE_VALUE)
    {
       printf("Open Hardware Key Failed\r\n");
       return FALSE;
    }
    l_Val = RegQueryValueEx(
              SwKey,
              (LPCTSTR)"Service",
              0,
              &dwRegType,
              (LPBYTE)szDATA,
              dwSizeRequired);
    if (l_Val == ERROR_SUCCESS)
    {
       	  printf("Device Service Name : %s\r\n", szDATA);
    } else {
       printf("Retrieve Device Service Name Failed\r\n");
    }  
    return B_Ret;
}

BOOL GetServiceData(
	LPCTSTR szServiceName,
	LPCTSTR szDataName,
       	LPTSTR szDATA,
       	DWORD dwLength,
       	PDWORD dwSizeRequired)
{
  LONG l_Val;
  HKEY SvcKey;
  char szTargetPath[MAX_PATH] = {'\0'};
  DWORD dwRegType = 0;
  BOOL B_Ret = FALSE;
  
  printf("Get Data for Service : %s\r\n", szServiceName);
  sprintf(szTargetPath,
  	"SYSTEM\\CurrentControlSet\\Services\\%s",
  	szServiceName);
  
  l_Val = RegOpenKeyEx(
  		HKEY_LOCAL_MACHINE,
  		(LPCTSTR)szTargetPath,
  		0,
  		KEY_ALL_ACCESS,
  		&SvcKey
	        );
  if (l_Val != ERROR_SUCCESS)
  {
  	printf("Can't access to Service : %s\r\n",
  		szServiceName);
  	return B_Ret;
  }
  
  l_Val = RegQueryValueEx(
              SvcKey,
              szDataName,
              NULL,
              NULL,
              (LPBYTE)szDATA,
              dwSizeRequired);
  
  if (l_Val == ERROR_SUCCESS)
  {
     printf("% = %\r\n", szDataName, szDATA);
     B_Ret = TRUE;
  } else {
     printf("Failed to retrieve %s\r\n, \
     		Data Read Out : %s\r\n \
     		Error Code = %d\r\n", \
     		szDataName, szDATA, GetLastError());
  }  
    
  return B_Ret;
}

BOOL GetServiceDataEx(
	LPCTSTR szServiceName,
	LPCTSTR szDataName,
       	LPTSTR szDATA,
       	DWORD dwLength,
       	PDWORD dwSizeRequired)
{
    SC_HANDLE  schSCManager;
    SC_HANDLE  schObject;
    LPQUERY_SERVICE_CONFIG lpqscBuf; 
    DWORD dwBytesNeeded; 
    
    //Connect To SCM 
    schSCManager = OpenSCManager( 
    			NULL, // local machine 
    			NULL, // ServicesActive database 
    			SC_MANAGER_ALL_ACCESS);  // full access rights 
    if (schSCManager == NULL)
    {
    	printf("Failed to open SCM Database\r\n");
    	return FALSE;
    }
    
    // Open a handle to the service. 
    schObject = OpenService( 
        		schSCManager, // SCManager database 
        		(LPCTSTR)szServiceName, // name of service 
        		SERVICE_QUERY_CONFIG); // need QUERY access 
    if (schObject == NULL)
    {
    	printf("Failed to open a handle \
    		to Service : %s\r\n", szServiceName);
	CloseServiceHandle(schSCManager);
    	return FALSE;
    }
    
    // Allocate a buffer for the configuration information
    lpqscBuf = (LPQUERY_SERVICE_CONFIG) LocalAlloc( 
        					LPTR, 4096); 
    if (lpqscBuf == NULL) 
    {
        printf("Failed to Allocate a buffer \
        	for the configuration information\r\n"); 
    	CloseServiceHandle(schObject);
	CloseServiceHandle(schSCManager);
    	return FALSE;
    }
    
    // Get the configuration information
    if (!QueryServiceConfig( 
        	schObject, 
        	lpqscBuf, 
        	4096, 
        	&dwBytesNeeded) ) 
    {
        printf("Failed to get the configuration information\r\n"); 
    	CloseServiceHandle(schObject);
	CloseServiceHandle(schSCManager);
	LocalFree(lpqscBuf);
    	return FALSE;
    }

    printf(" Binary path: %s\n", lpqscBuf->lpBinaryPathName); 
    strcpy(szDATA, lpqscBuf->lpBinaryPathName);
    //Close connection to Service itself
    CloseServiceHandle(schObject);
    //Close connection to SCM
    CloseServiceHandle(schSCManager);
    LocalFree(lpqscBuf);
    return TRUE;
}


BOOL GetDvcSvcNameByHwId(
	HKEY RootKey,
	LPCTSTR szTargetHardwareID,
	LPTSTR szData,
	DWORD dwLength)
{
    HKEY SubKey;
    char szSubKey[256] = {'\0'};
    LPTSTR buffer = NULL;
    LPTSTR p = NULL;
    char szHardwareID[1024] = {'\0'};
    int Index;
    BOOL B_Ret = FALSE;
    LONG l_Ret;
    DWORD dwRequiredSize, dwSizeForSubKey;
    
    dwSizeForSubKey = 256;
    
    printf("Search Device with H/W ID : %s\r\n", szTargetHardwareID);
    
    for (Index = 0; RegEnumKey(
    			RootKey,
    			Index,
    			szSubKey,
    			dwSizeForSubKey) == ERROR_SUCCESS;
    	Index++) {
    	// if the sub key doesn't stand for H/W instance
    	// make recursive call from this branch
    	if (szSubKey == '\0')
    	   break;
    	printf("Enumerate SubKey : %s\r\n", szSubKey);
	l_Ret = RegOpenKeyEx(
  			RootKey,
  			szSubKey,
  			0,
  			KEY_READ,
  			&SubKey); 
  	if (l_Ret == ERROR_SUCCESS) {
    	   if (IsNumeric(szSubKey) != TRUE) {
    	      //printf("It is not a H/W instance branch\r\n");
    	      B_Ret = GetDvcSvcNameByHwId(
    	   		 	SubKey, szTargetHardwareID,
    	   			szData, dwLength);
    	      if (B_Ret)
    	      {
    	      	 RegCloseKey(SubKey);
    	      	 if (szSubKey) LocalFree(szSubKey);
    	   	 break;
    	      }
    	   } else {//if (IsNumeric(szSubKey) != TRUE)
	   //The sub key is four digit instance ID
	   //Compare HardwareID
	      printf("It is a H/W instance branch\r\n");
	      dwRequiredSize = 1024;
	      l_Ret = RegQueryValueEx(
	   		   SubKey,
	   		   (LPCTSTR)"HardwareID",
	   		   NULL, 0,
	   		   szHardwareID,
	   		   &dwRequiredSize);
	      if (l_Ret == ERROR_SUCCESS)
	      {
	      	printf("Get Hardware ID in Registry Okay\r\n");
	      	for (p=szHardwareID;*p&&(p<&szHardwareID[dwRequiredSize]);p+=lstrlen(p)+sizeof(char))
	       	if (strcmp(szTargetHardwareID, p) == 0)
	      	{
	      	  //We find the righ entry for target device
	      	  //get service now
	      	  printf("Found Target Device!!\r\n");
	      	  dwRequiredSize = 256;
	      	  buffer = LocalAlloc(
                    LPTR, 256);
	      	  RegQueryValueEx(
	   		   SubKey,
	   		   (LPCTSTR)"Service",
	   		   NULL, 0,
	   		   buffer,
	   		   &dwRequiredSize);
	          strcpy(szData, buffer);
	          RegCloseKey(SubKey);
	          LocalFree(buffer);
	          printf("The Service for Device : %s\r\n", szData);
	          return TRUE;
	      	}
	      	RegCloseKey(SubKey);
		if (szSubKey) LocalFree(szSubKey);
	      } else {//if (l_Ret == ERROR_SUCCESS)
	        printf("Failed to Get Hardware ID in Registry\r\n");
	        RegCloseKey(SubKey);
	        return FALSE;
	      }
    	   }//end if (IsNumeric(szSubKey) != TRUE) {
    	   RegCloseKey(SubKey);
	}//if (l_Ret == ERROR_SUCCESS)
	ZeroMemory(szSubKey, 256);
    }//for
   
    return B_Ret;			
}

BOOL IsNumeric(LPTSTR szData)
{
  static char szDigitNum[10] = {
  	'0','1','2','3','4',
  	'5','6','7','8','9'};
  char *p = NULL;
  BOOL B_Ret = TRUE, B_AllNum = TRUE;
  int I,Index;
  
  for (p = szData, I = 0; p[I]; I++)
  {
     B_AllNum = FALSE;
     for (Index = 0; Index < 10; Index++)
     {
     	if (p[I] == szDigitNum[Index])
     	{
     	  B_AllNum = TRUE;
     	  break;
     	}
     }
     B_Ret = B_Ret && B_AllNum;
  }
  return B_Ret;
}